package org.lep.leetcode.wordladder;

import java.util.*;

/**
 * Source : https://oj.leetcode.com/problems/word-ladder-ii/
 *
 * Created by lverpeng on 2017/8/23.
 *
 * Given two words (start and end), and a dictionary, find all shortest transformation
 * sequence(s) from start to end, such that:
 *
 * Only one letter can be changed at a time
 * Each intermediate word must exist in the dictionary
 *
 * For example,
 *
 * Given:
 * start = "hit"
 * end = "cog"
 * dict = ["hot","dot","dog","lot","log"]
 *
 * Return
 *
 *   [
 *     ["hit","hot","dot","dog","cog"],
 *     ["hit","hot","lot","log","cog"]
 *   ]
 *
 * Note:
 *
 * All words have the same length.
 * All words contain only lowercase alphabetic characters.
 *
 */
public class WordLadder2 {

    /**
     * 在wordladder1的基础上，找到start到end的所有变化路径
     *
     * 这里需要回溯，采用深度优先DFS
     *
     * 优化：
     * 因为这需要回溯，也就是说可能会重复计算某个单词的neighbors，所以可以实现将所有的单词构造成一棵树,就不需要每次计算neighbors
     *
     * @param start
     * @param end
     * @param dict
     * @return
     */
    public List<List<String>> findLadders (String start, String end, String[] dict) {
        Set<String> set = new HashSet<String>(Arrays.asList(dict));
        set.add(end);
        List<List<String>> result = new ArrayList<List<String>>();
        Set<String> list = new HashSet<String>();
        list.add(start);
        List<String> ladder = new ArrayList<String>();

        recursion(list, end, ladder, result, set);
        return result;
    }

    public void recursion (Set<String> list, String end, List<String> ladder, List<List<String>> result, Set<String> set) {
        for (String str : list) {
            ladder.add(str);
            if (str.equals(end)) {
                result.add(new ArrayList<String>(ladder));
            }
            Set<String> neighbors = findNeighbors(str,set);
            recursion(neighbors, end, ladder, result, set);
            set.addAll(neighbors);
            ladder.remove(str);
        }
    }


    private Set<String> findNeighbors (String cur, Set<String> dict) {
        Set<String> neighbors = new HashSet<String>();
        for (int i = 0; i < cur.length(); i++) {
            for (int j = 0; j < 26; j++) {
                char ch = (char) ('a' + j);
                if (cur.charAt(i) != ch) {
                    String candidate = "";
                    if (i == cur.length()-1) {
                        candidate = cur.substring(0, i) + ch;
                    } else {
                        candidate = cur.substring(0, i) + ch + cur.substring(i+1);
                    }
                    if (dict.contains(candidate)) {
                        neighbors.add(candidate);
                        dict.remove(candidate);
                    }
                }
            }
        }
        return neighbors;
    }

    public static void print (List<List<String>> list) {
        for (List<String> strList : list) {
            System.out.println(Arrays.toString(strList.toArray(new String[strList.size()])));
        }
    }

    public static void main(String[] args) {
        WordLadder2 wordLadder2 = new WordLadder2();
        String start = "hit";
        String end = "cog";
        String[] dict = new String[]{"hot","dot","dog","lot","log"};
        print(wordLadder2.findLadders(start, end, dict));
    }
}
